File的本质是一个“文件地址标识”。内部维护着表示绝对路径的String。
private String path;
File不是单指某一个文件,而是 “绝对地址” 目录下的所有文件。“注意”:不迭代目录。只返回第一层的文件列表。
“绝对地址” 目录下的所有文件列表可以用 File#list() 方法得到。
public String[] list() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(path);
}
return fs.list(this);
}
这个方法调用的fs:指的是native数据FileSystem。所以返回文件列表的动作是系统替我们完成的。不是用Java实现的。
static private FileSystem fs = FileSystem.getFileSystem();
但我们还是可以对返回的List有所控制。通过给list()方法传入一个FilenameFilter对象做参数。
public String[] list(FilenameFilter filter) {
String names[] = list();
if ((names == null) || (filter == null)) {
return names;
}
List<String> v = new ArrayList<>();
for (int i = 0 ; i < names.length ; i++) {
if (filter.accept(this, names[i])) {
v.add(names[i]);
}
}
return v.toArray(new String[v.size()]);
}
list()方法会回调FilenameFilter对象的accept()方法,判断某个文件是否符合过滤条件。accept()方法用个正则表达式就行了。
File path = new File(".");
String[] list = path.list(new FilenameFilter() {
private Pattern pattern = Pattern.compile(args[0]);
public boolean accept(File dir, String name) {
return pattern.matcher(name).matches();
}
});
这是一个标准的“策略模式”的良好实践。
非常常用的功能。完全可以写进自己的类库。
具体实现参见本章练习4,5,6.
Java IO有60个类。但只要思路清晰,它们各自的用途还是很明确的。
首先要明确,IO有两大家族:
上两张全家福:
什么是面向字节?什么是面向字符?先要从最基本的概念说起:
基本单位一个”字节“占8个bit。这很简单。Java里的基本字符char基于UTF-16。和只占一个字节的ASCII码不同,常规字符占用两个字节,16 bit。
要搞懂各种编码之间的前世今生,推荐下面这篇比较好懂的文章: 《字符编码笔记:ASCII,Unicode和UTF-8》
简单讲就是,一个字节8 bit,最多为256个符号编码。所以英语26个字母,再加几个常用符号,标点,256个码位足够了。这就熟悉的ASCII码。如下图,ASCII码一共收录了空格及94个“可印刷字符”。
ASCII给英语用足够了。但其他的语言,比如拉丁语系的法语西班牙语等语言都有自己的特殊字母。所以,世界的每个不同地区又有了自己的特殊编码标准。比如ISO/IEC 8859-n系就是国际标准化组织定义的一系列8位字符集。也只占一个字节。最常用的比如ISO/IEC 8859-1就是法语,芬兰语所用的西欧字符集。
汉字有好几万个。编码一个8 bit,256个码位就不够了。所以我国的汉字编码现行标准是《GB 18030》。每个字可以由1个、2个或4个字节组成,编码空间有161万个字符。另一个中国常用编码集是Big5。全世界各国都有自己的《字元编码标准》。
但这样编码的缺点很明显,同一个文件,到了另一个国家,用另一套编码集来解码,会产生乱码。在全球化的今天,最好全世界有一套统一的字符编码集。这就是《Unicode》。Unicode的理念就是那么直接 – 每种不同语言的不同字符都有一个单独编码。最初,Unicode占用2个字节,最多编码256*256=65536个字符。但很快从3.1版本开始就不够用了。所以又搞出了附加字符集。附加字符用4个字节表示。目前Unicode已对总共超过12万个字符编码。
这里需要搞清楚一件事:Unicode只是一套符号的编码。但计算机具体怎么读取这套编码,又是另外一件事。
我们可以老老实实地用2个字节,16位来表示一个Unicode字符。每次老老实实读取两个字节。然后在读到某个特殊标记的时候,知道是附加字符集,需要再往后读2个字节。但由于英语是使用最广泛的语言,明明一个字符就能完成,现在非要用2个字符。浪费了内存空间。不符合编码的规范:常用字符用最短的编码。所以就要想办法怎么能用一个字节表示英语,其他字符用多个字节。而且又要让电脑能够识别,自动处理。这就是UTF-8做的事。一本正经下定义的话:关键就是可变长编码。
UTF-8编码规则,如下图。可变长编码的关键就是:怎么让电脑知道每次需要读多大长度。所以必然需要某种标识。
具体的规则,很简单,只有两条:
UTF-16和UTF-8的不同在于,它用2个字节表示一些常用字符,4个字节表示附加字符。
具体编码规则,参见《使用 Java 语言进行 Unicode 代理编程》这篇文章。
如果使用面向字节的流,那我们就必须自己处理上面介绍的从字节到字符的编码转换工作。比如根据UTF-16编码规则把byte[]数组解码成String字符串。虽然String类的构造器可以完成编码到字符映射的工作,一个String对象也有getBytes()方法反向编码。
String s = "我是沈玮";
byte[] b = s.getBytes("UTF-8");
String n = new String(b,"UTF-8");
但这样也增加了很多工作量。所以大多数情况应该优先使用面向字符的流。
关于Java的编码问题,也有一篇好的阅读材料:《深入分析 Java 中的中文编码问题》。
除了byte到char需要编码和解码,其他基本型也不是直接读取就可以的。不同数据类型有不同的长度,而且内部数据编排原理也各不相同。以双精度浮点数double为例,
首先,double型的长度为64 bit。占据8个字节。最高位63位表示正负号,62-52的高11位为指数位,51-0的低52位表示尾数。
对double型的解码,虽然不需要一个几十万规模的字符集,但也需要一个转换。
装饰器模式的基本思想很简单,就是利用组合和代理为类添加新功能。
比如一个最简单的容器模型Basic类。
class Basic {
private String value;
public void set(String val) { value = val; }
public String get() { return value; }
}
需要有一个所有装饰器的基类。把Basic类作为一个成员字段,以及一个以Basic类对象为参数的构造器。其实就是简单地用一个Basic对象做代理,封装所有方法。
class Decorator extends Basic {
protected Basic basic;
public Decorator(Basic basic) { this.basic = basic; }
public void set(String val) { basic.set(val); }
public String get() { return basic.get(); }
}
所有的装饰类继承原始装饰器基类后,可以在原有Basic基础上添加新功能。而它的构造器就是一个普通Basic类。
class TimeStamped extends Decorator {
private final long timeStamp;
public TimeStamped(Basic basic) {
super(basic);
timeStamp = new Date().getTime();
}
public long getStamp() { return timeStamp; }
}
Java用装饰器模式实现IO,主要是为了减少库中类的数量。通过拼装有限的功能组件,最终实现具有复合型功能的IO工具。
虽然Java IO包里类一点也不少,一共有60个类。但它们能够组合出来的具有不同功能的工具数量更是惊人。这么说,Java IO虽然有点难用,但还是挺成功的。
要理清IO包的脉络,一定要明确一个思路,IO包中的类分为两种不同的角色:
“核心IO”:负责直接读写特定类型数据源。以(InputStream,OutputStream),或者(Reader,Writer)为基类。 * File######: * String######: * ByteArray######(or: CharArray######): * Piped######: * Zip######:
“过滤器”:负责改变流的行为。以(FilterInputStream,FilterOutputStream),或者(FilterReader,FilterWriter)为基类。 * Buffered######: * Data######: * Print######: * Pushback######:
一般使用IO包的模式就是:在核心IO类的外面套过滤器。
考虑下面这个例子:
DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(new File(Data.txt))));
FileInputStream负责从本地文件读取字节流。然后套上一层BufferedInputStream缓冲器,避免频繁读取磁盘。最后还要套上DataInputStream装饰器,为的是方便读取不同类型的数据。
构造器层面,能够这样装配,因为FileInputStream有一个接受File为参数的构造器。BufferedInputStream和DataInputStream都有一个接受InputStream为参数的构造器。这就是装饰器模式的工作方式。
我们把DataInputStream套在最外面,最后暴露给我们的是DataInputStream类的接口,这是因为我们要用的就是DataInputStream所独有的比如readInt(), readDouble()这样方便读取数据的方法。
我们把BufferedInputReader夹在中间,但不用担心他的功能被覆盖掉。因为DataInputStream读取数据的方法,比如readInt()最终调用的都是read()方法(源代码如下):
public final int readInt() throws IOException {
int ch1 = in.read();
int ch2 = in.read();
int ch3 = in.read();
int ch4 = in.read();
if ((ch1 | ch2 | ch3 | ch4) < 0)
throw new EOFException();
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
}
因为int型占4个字节,所以调用4次read()方法。由于DataInputStream没有定义自己的无参数read()方法。所以根据其基类FilterInputStream类的无参数read()方法,直接调用的就是BufferedInputStream的read()方法。
public int read() throws IOException {
return in.read();
}
而BufferedInputStream的read()方法是实现了缓冲功能的。所以BufferedInputStream的缓冲作用也得以发挥。同时又拥有了DataInputStream的特殊数据读取接口。
public synchronized int read() throws IOException {
if (pos >= count) {
fill();
if (pos >= count)
return -1;
}
return getBufIfOpen()[pos++] & 0xff;
}
所以一般把DataInputStream或者PrintStream这种拥有特殊接口的类套在最外面,然后把BufferedInputStream夹在中间,最后由FileInputStream或者StringInputStream负责直接面对数据源,是套嵌多层过滤器的通用做法。
作为最常用的过滤器,每个流不管是输入还是输出都会被套上一层“缓冲器”。缓冲器的原理其实很简单:一次性读取很多数据到内存,作为缓存,以后每次read就从缓存里取,而不是每次都磁盘操作读取实际文件。输出缓冲的原理也是一样,多攒一点内容才执行实际读写。下面代码是BufferedInputStream的部分源码:
public class BufferedInputStream extends FilterInputStream {
private static int defaultBufferSize = 8192;
/**
* The internal buffer array where the data is stored. When necessary,
* it may be replaced by another array of
* a different size.
*/
protected volatile byte buf[];
//... ...
}
可以看到,BufferedInputStream的缓冲区是一个字节数组byte buf[]。默认大小是8192字节,总共64k bit。
下面是它的read()方法。很简单,如果缓存读完了,就执行fill()方法重新填充。否则就直接用getBufIfOpen()方法读取缓存数组。其中pos作为数组的游标。
public synchronized int read() throws IOException {
if (pos >= count) { //缓存读完
fill();
if (pos >= count)
return -1;
}
return getBufIfOpen()[pos++] & 0xff;
}
getBufIfOpen()方法就是简单返回缓存数组。
/**
* Check to make sure that buffer has not been nulled out due to
* close; if not return it;
*/
private byte[] getBufIfOpen() throws IOException {
byte[] buffer = buf;
if (buffer == null)
throw new IOException("Stream closed");
return buffer;
}
fill()方法稍微复杂一点,但抛开前面的各种安全检测,最后就是用getInIfOpen()方法获取它装饰的输入流。然后调用被装饰输入流的read()方法读取数据,放到缓存buffer里。
/**
* Fills the buffer with more data, taking into account
* shuffling and other tricks for dealing with marks.
* Assumes that it is being called by a synchronized method.
* This method also assumes that all data has already been read in,
* hence pos > count.
*/
private void fill() throws IOException {
byte[] buffer = getBufIfOpen();
if (markpos < 0)
pos = 0; /* no mark: throw away the buffer */
else if (pos >= buffer.length) /* no room left in buffer */
if (markpos > 0) { /* can throw away early part of the buffer */
int sz = pos - markpos;
System.arraycopy(buffer, markpos, buffer, 0, sz);
pos = sz;
markpos = 0;
} else if (buffer.length >= marklimit) {
markpos = -1; /* buffer got too big, invalidate mark */
pos = 0; /* drop buffer contents */
} else { /* grow buffer */
int nsz = pos * 2;
if (nsz > marklimit)
nsz = marklimit;
byte nbuf[] = new byte[nsz];
System.arraycopy(buffer, 0, nbuf, 0, pos);
if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
// Can't replace buf if there was an async close.
// Note: This would need to be changed if fill()
// is ever made accessible to multiple threads.
// But for now, the only way CAS can fail is via close.
// assert buf == null;
throw new IOException("Stream closed");
}
buffer = nbuf;
}
count = pos;
int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
if (n > 0)
count = n + pos;
}
把握住上面这个组合装饰器的原则,java IO的一些习惯用法就很好理解了。
一般Unicode编码的文本文件,可以用面向字符的FileReader读取,再套一个缓冲器BufferedReader。
BufferedReader br = new BufferedReader(new FileReader(new File(Data.txt)));
一般把数据封装成String,然后用StringReader读取。
String s = "Hello World!";
StringReader br = new StringReader(s);
格式化读取就必须用DataInputStream。因为DataInputStream是面向字节的,可以用把String打散成byte[]数组,然后用ByteArrayInputStream读取。
String s = "Hello World!";
DataInputStream in = new DataInputStream(new ByteArrayInputStream(s.getBytes()));
写入本地文件,最好用PrintWriter。
PrintWriter pr = new PrintWriter(new BufferedWriter(new FileWriter(new File(Data.txt))));
PrintWriter有一个直接接受File为参数的构造器。
PrintWriter pr = new PrintWriter(new File(Data.txt));
到目前为止,所有的描述到了附着于具体数据读写对象的“核心IO”类就结束了。具体的读写操作被想象成一个“黑箱”过程。这里,以FileInputStream为例,解释一下,一个本地文件到底是怎么被打开,然后读取到内存的。此处主要参考了[** 《JNI探秘—–你不知道的FileInputStream的秘密》 ](http://www.cnblogs.com/zuoxiaolong/p/jni1.html)这篇文章。感谢作者 – **左潇龙。
先看FileInputStream源码中的一小段:
public class FileInputStream extends InputStream {
/* File Descriptor - handle to the open file */
private FileDescriptor fd;
private FileChannel channel = null;
public FileInputStream(String name) throws FileNotFoundException {
this(name != null ? new File(name) : null);
}
public FileInputStream(File file) throws FileNotFoundException {
String name = (file != null ? file.getPath() : null);
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(name);
}
if (name == null) {
throw new NullPointerException();
}
fd = new FileDescriptor();
open(name);
}
//... ...
}
这段代码有两个重点:
FileDescriptor类里最关键的是这个叫handle的字段。就是我们打开文件的句柄(handle)。
public final class FileDescriptor {
private int fd;
private long handle;
//... ...
}
而open()方法是个native方法。如下所示,内部就是简单调用了另一个系统方法fileOpen()。
JNIEXPORT void JNICALL
Java_java_io_FileInputStream_open(JNIEnv *env, jobject this, jstring path) {
fileOpen(env, this, path, fis_fd, O_RDONLY);
}
而Windows系统fileOpen()方法的作用就是:获得目标文件的句柄,并赋值给handle字段。
/*
env是一个指向JAVA本地方法环境的指针,它的作用大部分用来获取环境参数,比如当前线程。
this相信大家都不陌生,这就是指的当前FileInputStream的实例,只不过在C/C++环境中,它是jobject类型
path就是文件路径了,也是我们传进来的name参数
fid是FileInputStream类中fd属性的地址偏移量
flags是打开文件的方式,一般就是只读方式。
*/
void fileOpen(JNIEnv *env, jobject this, jstring path, jfieldID fid, int flags)
{
jlong h = winFileHandleOpen(env, path, flags);//这一句话就得到了一个文件的句柄
if (h >= 0) {
SET_FD(this, h, fid);//这一句话就是将这个句柄赋给了FileDescriptor类的handle属性
}
}
所以打开一个文件的本质很简单,就是获得这个文件在系统中的“句柄”,然后根据这个句柄,顺藤摸瓜找到这个文件在磁盘上的物理地址。最后对磁盘进行读写。
和老io包的 “面向流” 读写不同,引入java.nio包是 “面向缓冲区” 的读写。
“面向流”之前已经讲了,就是老IO包所有操作的的底层都是在InputStream和OutputStream的流上进行操作。上文已经分析过,比如我们打开文件,其实就是直接获取此文件在系统上的句柄,然后通过系统调用对文件进行读写。中间没有缓冲(BufferedReader那是通过装饰器套上的)。
而“面向缓冲区”的结构由两个部分组成:一个是管道“FileChannel”,一个是缓冲区“ByteBuffer”。他们两个就像是 “煤矿隧道” 和 “拉煤车” 的关系。至于样的模型为什么要比传统面向流的模型要高效,背后涉及两个不同的I/O模型。
旧IO包采用的是传统阻塞式IO模式。如下图所示,阻塞式IO就是用户进程在系统调用读写文件时,保持“阻塞”。大白话讲就是知道文件完整地读写完之前,用户进程一直处在等待状态。
新IO采用的是复用I/O模型。简单讲就是第一个select步骤就是用户问系统他要的文件有没有准备好。之后的recvfrom才是真正的文件复制阶段。
至于这个复用I/O模型好在哪儿,关键就在于它允许内核同时管理多条“管道”。最基本的是一个叫Reactor的模式。
用户向内核注册感兴趣的事件(用register()方法),由内核维护一个负责轮询的线程,在某个同道准备好之后,通知用户。 nio多路复用的架构几乎就是UNIX内核的“管道-缓冲”架构的翻版。主要是为了更好地充分利用底层系统调用。实际上NIO在性能上已经几乎达到了它能达到的最佳性能。
一张图,看清旧io包和nio包的区别。映证了NIO使用了Reactor模式。
注意FileChannel并没有实现SelectableChannel接口,因此不支持Selector。只有用于网络传输的ServerSocketChannel才实现了SelectableChannel接口。 下面这个清单演示了一次创建Channel,Selector,以及Channel向Selector注册感兴趣事件,获得注册钥的过程。
//创建Selector
Selector selector = Selector.open();
//创建Channel
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking( false );
ServerSocket ss = ssc.socket();
InetSocketAddress address = new InetSocketAddress( ports[i] );
ss.bind( address );
//Channel向Selector注册感兴趣事件
SelectionKey key = ssc.register( selector, SelectionKey.OP_ACCEPT );
接下来,用Selector#selectedKey()方法监听发生的感兴趣事件,对返回的就绪的SelectionKey一一处理。
int num = selector.select();
Set selectedKeys = selector.selectedKeys();
Iterator it = selectedKeys.iterator();
while (it.hasNext()) {
SelectionKey key = (SelectionKey)it.next();
// ... deal with I/O event ...
}
通过mark,position,limit,capacity四个标量控制缓冲区的读写。可以前进,可以后退,比传统流只能一位位往下读写更灵活。
一篇好文章:** 《深入浅出 MappedByteBuffer》 **
既然类和对象的本质就是数据的封装。那么无论是类还是对象,本质都是一组结构化的数据,也就是都可以序列化。
支持序列化的类必须实现 “Serializable” 接口。它只是一个标记性接口,没有定义任何方法。
一个序列化的对象可以被编码成流写入磁盘,“持久”储存。并且对象序列化保存的是对象的“全景图”和它所包含的所有引用。
对象序列化具体是一个很复杂的过程,尽量不要自己重复造轮子。用系统自带的 “ObjectOutputStream” 和 “ObjectInputStream” 就可以。
对象的“持久化”似乎为OO编程在工程上打开了一扇新的大门。尤其是网络编程,这让远距离传输对象变得非常方便。只要拥有某个类的.class文件,就能远距离复活这个类的对象。
Externalizable接口继承自Serializable接口。可以在序列化和反序列化过程中提供更多的控制。
实现Externalizable接口的类在被反序列化的时候,不是直接通过字节码来全盘复制和重建对象,而是调用默认构造器,重新构造。
接口通过定义 WriteExternal(ObjectOutput out) 和 ReadExternal(ObjectInput in) 两个方法。分别接受输入,输出流为参数,目的是允许追加序列化某些需要被序列化的部件。所以更灵活。
transient 关键字用来“定点关闭”某些不想被序列化的“敏感字段”,比如说密码。
要持久化某些数据或者部件,序列化并不是唯一的选择。另一种更轻量级的实现是Preferences API。
关于Prefecrences API的一篇文章:《用 Preferences API 存储对象》
它有点类似Windows的注册表。这里对它的细节深挖。但它确实可以在系统层面持久储存某些信息。比如基本型,以及String。但它只能做这么多。
数据结构层面,Preferences是一系列“键-值”对的节点。一共有两棵树”用户树”和”系统树“。分别用userNodeForPackage()和systemNodeForPackage()。这两棵树功能是一样的。
具体这些数据被储存到哪里了?对不同的操作系统,Java使用的是不同的系统资源。对于Windows系统,就是存在了注册表里。
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
import java.util.regex.*;
public class Exercise1 {
public static void main(final String[] args) {
File path = new File("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter17/testframework/gen/");
String[] list;
final String p;
if(args.length == 0){
p=".*gen.*";
}else{
p=args[0];
}
list = path.list(new FilenameFilter() {
private Pattern pattern = Pattern.compile(p);
public boolean accept(File dir, String name) {
boolean result=false;
try{
BufferedReader br=new BufferedReader(new FileReader(new File(dir.getAbsolutePath()+"/"+name)));
while(true){
String line=br.readLine();
if(line==null){break;}
if(pattern.matcher(line).matches()){
result=true;
}
}
}catch(Exception e){
System.out.println("Can not open file: "+dir.getAbsolutePath()+name);
}
return result;
}
});
Arrays.sort(list, String.CASE_INSENSITIVE_ORDER);
for(String dirItem : list)
System.out.println(dirItem+": contains "+p);
}
}
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
import java.util.regex.*;
public class Exercise2 {
public static class SortedDirList{
private File path;
private String[] list;
public SortedDirList(String absPath){
path=new File(absPath);
list = path.list();
}
public String[] list(){return list;}
public String[] list(String regex){
list = path.list(new FilenameFilter() {
private Pattern pattern = Pattern.compile(regex);
public boolean accept(File dir, String name) {
return pattern.matcher(name).matches();
}
});
Arrays.sort(list, String.CASE_INSENSITIVE_ORDER);
return list;
}
}
public static void main(final String[] args) {
Exercise2.SortedDirList dl=new Exercise2.SortedDirList("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter17/testframework/gen/");
String[] fullList=dl.list();
String[] filteredList=dl.list(".*Ran.*");
System.out.println(">>>FULL LIST<<<\n"+Arrays.toString(fullList)+"\n");
System.out.println(">>>GEN LIST<<<\n"+Arrays.toString(filteredList));
}
}
import java.util.*;
import java.io.*;
import java.util.regex.*;
public class Exercise3 {
public static class SortedDirList{
private File path;
private String[] list;
public SortedDirList(String absPath){
path=new File(absPath);
list = path.list();
}
public String[] list(){return list;}
public String[] list(String regex){
list = path.list(new FilenameFilter() {
private Pattern pattern = Pattern.compile(regex);
public boolean accept(File dir, String name) {
return pattern.matcher(name).matches();
}
});
Arrays.sort(list, String.CASE_INSENSITIVE_ORDER);
return list;
}
public File getPath(){return path;}
}
public static int countLine(File dir, String[] list){
if(list.length==0){return 0;}
int result=0;
for(String name:list){
try{
BufferedReader br=new BufferedReader(new FileReader(new File(dir+"/"+name)));
while(br.readLine()!=null){result++;}
}catch(Exception e){
System.out.println("Can not open the file: "+dir+"/"+name);
}
}
return result;
}
public static void main(final String[] args) {
Exercise3.SortedDirList dl=new Exercise3.SortedDirList("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter17/testframework/gen/");
String[] fullList=dl.list();
String[] filteredList=dl.list(".*Ran.*");
System.out.println(">>>FULL LIST<<<\n"+Arrays.toString(fullList)+"\n");
System.out.println(">>>GEN LIST<<<\n"+Arrays.toString(filteredList)+"\n");
System.out.println("Total Line: "+Exercise3.countLine(dl.getPath(),filteredList));
}
}
Exercise 4: (2) Use Directory.walk( ) to sum the sizes of all files in a directory tree whose names match a particular regular expression.
Exercise 5: (1) Modify ProcessFiles.java so that it matches a regular expression rather than a fixed extension.
Exercise 6: (5) Use ProcessFiles to find all the Java source-code files in a particular directory subtree that have been modified after a particular date.
简化练习4中的Directory类。
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
import java.util.regex.*;
public class Directory{
private List<File> files=new ArrayList<File>();
private List<File> dirs=new ArrayList<File>();
private int size=0;
private int lines=0;
public Directory(){this(".");}
public Directory(String dir){this(new File(dir));}
public Directory(File dir){this(dir,".*");}
public Directory(String dir, String regex){this(new File(dir),regex);}
public Directory(File dir,String regex){
recurseDirs(dir,new FilenameFilter(){
private Pattern p=Pattern.compile(regex);
public boolean accept(File dir,String name){
return p.matcher(name).matches();
}
});
statistic();
}
public Directory(File dir,FilenameFilter filter){
recurseDirs(dir,filter);
statistic();
}
//递归收集文件
public void recurseDirs(File dir, FilenameFilter filter){
File[] children=dir.listFiles();
if(children==null || children.length==0){return;}
for(File f:children){
if(f.isDirectory()){
dirs.add(f);
recurseDirs(f,filter);
}else{
if(filter.accept(dir,f.getName())){
files.add(f);
}
}
}
}
public void statistic(){
if(files!=null && files.size()>0){
size=files.size();
int num=0;
for(File file:files){
try{
BufferedReader bf=new BufferedReader(new FileReader(file));
while(bf.readLine()!=null){
num++;
}
}catch(Exception e){
System.out.println(file.getName()+" can not open!");
}
}
lines=num;
}
}
public List<File> getFile(){return files;}
public List<File> getDir(){return dirs;}
public void showStatistic(){System.out.println("File number: "+size+"\n"+"Total Lines: "+lines+"\n");}
}
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
import java.util.regex.*;
public class Exercise4{
public static void main(String[] args){
Directory d=new Directory("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter17/testframework/",".*Gen.*");
d.showStatistic();
List<File> files=d.getFile();
List<File> dirs=d.getDir();
if(files!=null && !(files.size()==0)){
System.out.println(files);
}
if(dirs!=null && !(dirs.size()==0)){
System.out.println(dirs);
}
}
}
ProcessFiles配合Directory使用。由Directory递归读取文件,然后由ProcessFiles里的Strategy模块处理。
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
import java.util.regex.*;
public class ProcessFiles {
public interface Strategy {
void process(File file);
}
private Strategy strategy;
private String ext;
public ProcessFiles(Strategy strategy, String ext) {
this.strategy = strategy;
this.ext = ext;
}
public void start(String[] args){
try{
if(args.length==0){
processDirectoryTree(new File("."));
}else{
for(String arg:args){
processDirectoryTree(new File(arg));
}
}
}catch(IOException e) {
throw new RuntimeException(e);
}
}
public void processDirectoryTree(File root) throws IOException {
for(File file : new Directory(root.getAbsolutePath(), ".*\\." + ext).getFile()){
strategy.process(file.getCanonicalFile());
}
}
// Demonstration of how to use it:
public static void main(String[] args) {
new ProcessFiles(new ProcessFiles.Strategy() {
public void process(File file) {
System.out.println(file);
}
}, "java").start(args);
}
}
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
import java.util.regex.*;
public class Exercise5{
public static void main(String[] args){
new ProcessFiles(new ProcessFiles.Strategy() {
public void process(File file) {
System.out.println(file);
}
}, "ja.*").start(args);
}
}
}
用Directory和ProcessFile,对File做时间过滤。加了两个“时间-日期”转换方法。
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
import java.util.regex.*;
import java.text.*;
public class Exercise6{
public static void filterByModifyDate(String[] args, String date){
new ProcessFiles(new ProcessFiles.Strategy() {
private long time=getNanoTime(date);
public void process(File file) {
if(file.lastModified()>time){
System.out.println(file+" >>>Last Modified: "+getFormatDate(file.lastModified()));
}
}
}, "java").start(args);
}
public static long getNanoTime(String date){
try{
DateFormat dateFormat1 = new SimpleDateFormat("yyyy-MM-dd");
Date myDate = dateFormat1.parse(date);
return myDate.getTime();
}catch(ParseException pe){
System.out.println("Error when parsing Date!");
}
return 0l;
}
public static String getFormatDate(long millitime){
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return df.format(millitime).toString();
}
public static void main(String[] args){
filterByModifyDate(args,"2016-09-29");
}
}
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
import java.util.regex.*;
public class Exercise7{
public static void main(String[] args){
File file=new File("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/Directory.java");
LinkedList<String> list=new LinkedList<String>();
BufferedReader bf=null;
try{
bf=new BufferedReader(new FileReader(file));
while(true){
String line=bf.readLine();
if(line==null){break;}
list.add(line);
}
}catch(FileNotFoundException e){
System.out.println(file.getName()+" not found!");
}catch(IOException ioe){
System.out.println("have problem when reading lines... ...");
}finally{
if(bf!=null){
try{
bf.close();
}catch(IOException ioe){
System.out.println("File can not be close!");
}
}
}
int size=list.size();
for(int i=0;i<list.size();i++){
System.out.println(list.get(--size));
}
}
}
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
import java.util.regex.*;
public class Exercise8{
public static void inversePrint(File file){
LinkedList<String> list=new LinkedList<String>();
BufferedReader bf=null;
try{
bf=new BufferedReader(new FileReader(file));
while(true){
String line=bf.readLine();
if(line==null){break;}
list.add(line);
}
}catch(FileNotFoundException e){
System.out.println(file.getName()+" not found!");
}catch(IOException ioe){
System.out.println("have problem when reading lines... ...");
}finally{
if(bf!=null){
try{
bf.close();
}catch(IOException ioe){
System.out.println("File can not be close!");
}
}
}
int size=list.size();
for(int i=0;i<list.size();i++){
System.out.println(list.get(--size));
}
}
public static void main(String[] args){
if(args.length==0){
Exercise8.inversePrint(new File("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/Directory.java"));
}else{
for(String arg:args){
Exercise8.inversePrint(new File(arg));
}
}
}
}
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
import java.util.regex.*;
public class Exercise9{
public static void inversePrint(File file){
LinkedList<String> list=new LinkedList<String>();
BufferedReader bf=null;
try{
bf=new BufferedReader(new FileReader(file));
while(true){
String line=bf.readLine();
if(line==null){break;}
list.add(line.toUpperCase());
}
}catch(FileNotFoundException e){
System.out.println(file.getName()+" not found!");
}catch(IOException ioe){
System.out.println("have problem when reading lines... ...");
}finally{
if(bf!=null){
try{
bf.close();
}catch(IOException ioe){
System.out.println("File can not be close!");
}
}
}
int size=list.size();
for(int i=0;i<list.size();i++){
System.out.println(list.get(--size));
}
}
public static void main(String[] args){
if(args.length==0){
Exercise9.inversePrint(new File("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/Directory.java"));
}else{
for(String arg:args){
Exercise9.inversePrint(new File(arg));
}
}
}
}
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
import java.util.regex.*;
public class Exercise10{
public static void inversePrint(File file, String inWord){
LinkedList<String> list=new LinkedList<String>();
BufferedReader bf=null;
try{
bf=new BufferedReader(new FileReader(file));
String line=null;
while((line=bf.readLine())!=null){
String[] words=line.split("[\\W]+");
if(words.length>0){
for(String word:words){
if(word.toLowerCase().equals(inWord)){
list.add(line);
break;
}
}
}
}
}catch(FileNotFoundException e){
System.out.println(file.getName()+" not found!");
}catch(IOException ioe){
System.out.println("have problem when reading lines... ...");
}finally{
if(bf!=null){
try{
bf.close();
}catch(IOException ioe){
System.out.println("File can not be close!");
}
}
}
int size=list.size();
for(int i=0;i<list.size();i++){
System.out.println(list.get(--size));
}
}
public static void main(String[] args){
if(args.length!=2){
System.out.println("Please check the args!");
}else{
Exercise10.inversePrint(new File(args[0]),args[1]);
}
}
}
使用了注册工厂模式。
基层设施
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
public class Controller {
// A class from java.util to hold Event objects:
private List<Event> eventList = new ArrayList<Event>();
public void addEvent(Event c) { eventList.add(c); }
public void run() {
while(eventList.size() > 0)
// Make a copy so you’re not modifying the list
// while you’re selecting the elements in it:
for(Event e : new ArrayList<Event>(eventList))
if(e.ready()) {
System.out.println(e);
e.action();
eventList.remove(e);
}
}
public List<Event> getList(){return eventList;}
}
基层设施
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
public abstract class Event {
private long eventTime;
protected final long delayTime;
public Event(long delayTime) {
this.delayTime = delayTime;
start();
}
public void start() { // Allows restarting
eventTime = System.nanoTime() + delayTime;
}
public boolean ready() {
return System.nanoTime() >= eventTime;
}
public abstract void action();
}
Controller和Event的综合体。 内部有一个Controller字段,每个Event都是一个内部类。
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
public class GreenhouseControls {
private static Controller con=new Controller();
public Controller getController(){return con;}
private static boolean light = false;
public static class LightOn extends Event {
public static class Factory implements SmallEventFactory<LightOn>{
public LightOn create(long delayTime){return new LightOn(delayTime);}
}
public LightOn(long delayTime) { super(delayTime); }
public void action() {
// Put hardware control code here to
// physically turn on the light.
light = true;
}
public String toString() { return "Light is on"; }
}
public static class LightOff extends Event {
public static class Factory implements SmallEventFactory<LightOff>{
public LightOff create(long delayTime){return new LightOff(delayTime);}
}
public LightOff(long delayTime) { super(delayTime); }
public void action() {
// Put hardware control code here to
// physically turn off the light.
light = false;
}
public String toString() { return "Light is off"; }
}
private static boolean water = false;
public static class WaterOn extends Event {
public static class Factory implements SmallEventFactory<WaterOn>{
public WaterOn create(long delayTime){return new WaterOn(delayTime);}
}
public WaterOn(long delayTime) { super(delayTime); }
public void action() {
// Put hardware control code here.
water = true;
}
public String toString() {
return "Greenhouse water is on";
}
}
public static class WaterOff extends Event {
public static class Factory implements SmallEventFactory<WaterOff>{
public WaterOff create(long delayTime){return new WaterOff(delayTime);}
}
public WaterOff(long delayTime) { super(delayTime); }
public void action() {
// Put hardware control code here.
water = false;
}
public String toString() {
return "Greenhouse water is off";
}
}
private static String thermostat = "Day";
public static class ThermostatNight extends Event {
public static class Factory implements SmallEventFactory<ThermostatNight>{
public ThermostatNight create(long delayTime){return new ThermostatNight(delayTime);}
}
public ThermostatNight(long delayTime) {
super(delayTime);
}
public void action() {
// Put hardware control code here.
thermostat = "Night";
}
public String toString() {
return "Thermostat on night setting";
}
}
public static class ThermostatDay extends Event {
public static class Factory implements SmallEventFactory<ThermostatDay>{
public ThermostatDay create(long delayTime){return new ThermostatDay(delayTime);}
}
public ThermostatDay(long delayTime) {
super(delayTime);
}
public void action() {
// Put hardware control code here.
thermostat = "Day";
}
public String toString() {
return "Thermostat on day setting";
}
}
// An example of an action() that inserts a
// new one of itself into the event list:
public static class Bell extends Event {
public static class Factory implements SmallEventFactory<Bell>{
public Bell create(long delayTime){return new Bell(delayTime);}
}
public Bell(long delayTime) { super(delayTime); }
public void action() {
con.addEvent(new Bell(delayTime));
}
public String toString() { return "Bing!"; }
}
public static class Restart extends Event {
public static class Factory implements BigEventFactory<Restart>{
public Restart create(long delayTime, Event[] eventList){return new Restart(delayTime, eventList);}
}
private Event[] eventList;
public Restart(long delayTime, Event[] eventList) {
super(delayTime);
this.eventList = eventList;
for(Event e : eventList)
con.addEvent(e);
}
public void action() {
for(Event e : eventList) {
e.start(); // Rerun each event
con.addEvent(e);
}
start(); // Rerun this Event
con.addEvent(this);
}
public String toString() {
return "Restarting system";
}
}
public static class Terminate extends Event {
public static class Factory implements SmallEventFactory<Terminate>{
public Terminate create(long delayTime){return new Terminate(delayTime);}
}
public Terminate(long delayTime) { super(delayTime); }
public void action() { System.exit(0); }
public String toString() { return "Terminating"; }
}
}
工厂接口
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
public interface EventFactory<T>{}
工厂接口。因为Restart类的create()方法有两个参数,所以分化出一个工厂类。
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
public interface BigEventFactory<T>{
public T create(long delayTime, Event[] eventList);
}
工厂接口。正常Event的工厂接口。
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
public interface SmallEventFactory<T>{
public T create(long delayTime);
}
主要是Event的注册工厂。还有一个从外部文件读取EventList的loadEvents()方法。
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
public class ControllerHelper {
@SuppressWarnings("rawtypes")
private static Map<String,SmallEventFactory<? extends Event>> smallEventFactories=new HashMap<String,SmallEventFactory<? extends Event>>();
@SuppressWarnings("rawtypes")
private static Map<String,BigEventFactory<? extends Event>> bigEventFactories=new HashMap<String,BigEventFactory<? extends Event>>();
static {
smallEventFactories.put("LightOn",new GreenhouseControls.LightOn.Factory());
smallEventFactories.put("LightOff",new GreenhouseControls.LightOff.Factory());
smallEventFactories.put("WaterOn",new GreenhouseControls.WaterOn.Factory());
smallEventFactories.put("WaterOff",new GreenhouseControls.WaterOff.Factory());
smallEventFactories.put("ThermostatNight",new GreenhouseControls.ThermostatNight.Factory());
smallEventFactories.put("ThermostatDay",new GreenhouseControls.ThermostatDay.Factory());
smallEventFactories.put("Bell",new GreenhouseControls.Bell.Factory());
bigEventFactories.put("Restart",new GreenhouseControls.Restart.Factory());
smallEventFactories.put("Terminate",new GreenhouseControls.Terminate.Factory());
}
public static Event[] loadEvents(String path){
File file=new File(path);
List<Event> list=new ArrayList<Event>();
BufferedReader bf=null;
try{
bf=new BufferedReader(new FileReader(file));
String line=null;
while((line=bf.readLine())!=null){
String[] param=line.split("\\t");
if(param.length!=2){
System.out.println("Args Error: "+line);
}else{
System.out.println(param[0]+","+param[1]);
list.add(smallEventFactories.get(param[0]).create(Integer.parseInt(param[1])));
}
}
}catch(FileNotFoundException e){
System.out.println(file.getName()+" not found!");
}catch(IOException ioe){
System.out.println("have problem when reading lines... ...");
}finally{
if(bf!=null){
try{
bf.close();
}catch(IOException ioe){
System.out.println("File can not be close!");
}
}
}
System.out.println(list);
return list.toArray(new Event[list.size()]);
}
}
最后的测试类。
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
public class Exercise11{
public static void main(String[] args){
GreenhouseControls c=new GreenhouseControls();
Controller controller=c.getController();
controller.addEvent((Event)(ControllerHelper.smallEventFactories.get("Bell").create(900)));
Event[] eventList = ControllerHelper.loadEvents("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/Event.txt");
controller.addEvent((Event)(ControllerHelper.bigEventFactories.get("Restart").create(2000,eventList)));
controller.addEvent((Event)(ControllerHelper.smallEventFactories.get("Terminate").create(1000)));
controller.run();
}
}
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
public class Exercise12{
public static void copyFile(File inFile, File outFile){
BufferedReader in=null;
PrintWriter out=null;
try{
//input
in=new BufferedReader(new FileReader(inFile));
//output
if(!outFile.exists()){
outFile.createNewFile();
}
out=new PrintWriter(new BufferedWriter(new FileWriter(outFile)));
int count=0;
String line;
while((line=in.readLine())!=null){
out.println("Line "+ ++count +":\t"+line);
}
}catch(IOException ioe){
System.out.println("Check your inFile and outFile!");
}finally{
try{
in.close();
out.close();
}catch(IOException e){
System.out.println("Files cannot be closed correctly!");
}
}
}
public static void main(String[] args){
if(args.length!=2){
System.out.println("Check your arguments!");
}else{
copyFile(new File(args[0]), new File(args[1]));
}
}
}
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
public class Exercise13 {
static String file = "/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/BasicFileOutput.txt";
public static void main(String[] args) throws IOException {
LineNumberReader in = new LineNumberReader(new FileReader(new File("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/Exercise13.java")));
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)));
int lineCount = 1;
String s;
while((s = in.readLine()) != null ){
out.println(in.getLineNumber() + ": " + s);
}
out.close();
}
}
实验结果显示加了BufferedWriter装饰器之后比原先FileWriter效率高了20-25%。
需要当心,不要把最初两次测试结果统计进最后结果。因为最初的两轮耗时明显地长,尤其是第一次跑,是明显的离群点。这会导致先跑的Writer吃亏。
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
public class Exercise14 {
private static String inFile = "/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/BufferedInputFile.java";
public static long test(Writer out) throws IOException{
BufferedReader in = new BufferedReader(new FileReader(new File(inFile)));
int lineCount = 1;
String s;
long begin;
long end;
long result=0;
while((s = in.readLine()) != null ){
begin=System.nanoTime();
for(int i=0;i<10000;i++){
out.write(lineCount++ + ": " + s);
}
result+=(System.nanoTime()-begin);
}
out.close();
return result;
}
public static void main(String[] args) throws IOException {
long timeBuffer=0;
long timeNot=0;
for(int i=0;i<20;i++){
FileWriter fileOut=new FileWriter("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/BufferedInputFile.txt");
BufferedWriter bufferedOut = new BufferedWriter(new FileWriter("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/BufferedInputFile2.txt"));
if(i>=5){
timeBuffer+=test(bufferedOut);
timeNot+=test(fileOut);
}
}
System.out.println("Buf: "+timeBuffer/15);
System.out.println("Not: "+timeNot/15);
System.out.println((double)timeBuffer/timeNot);
}
}
Buf: 27568796
Not: 37021046
0.7446790010523365
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
public class Exercise15 {
public static void write(String file) throws IOException{
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
out.writeBoolean(true);
out.writeByte(56);
out.writeChar('a');
out.writeInt(56);
out.writeLong(56l);
out.writeShort(56);
out.writeFloat(56f);
out.writeDouble(3.14159);
out.writeUTF("That was pi");
out.close();
}
public static void read(String file) throws IOException{
DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
boolean b=in.readBoolean();
byte bt=in.readByte();
char c=in.readChar();
int i=in.readInt();
long l=in.readLong();
short s=in.readShort();
float f=in.readFloat();
double d=in.readDouble();
String str=in.readUTF();
in.close();
System.out.println("["+b+"],"+"["+bt+"],"+"["+c+"],"+"["+i+"],"+"["+l+"],"+"["+s+"],"+"["+f+"],"+"["+d+"],"+"["+str+"]");
}
public static void main(String[] args) throws IOException{
String file="/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/Data.txt";
write(file);
read(file);
}
}
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
public class Exercise16 {
static String file = "/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/Data2.txt";
static void display() throws IOException {
RandomAccessFile in = new RandomAccessFile(file, "r");
for(int i = 0; i < 7; i++){
System.out.println("Value " + i + ": " + in.readDouble());
}
System.out.println(in.readUTF());
boolean b=in.readBoolean();
byte bt=in.readByte();
char c=in.readChar();
int i=in.readInt();
long l=in.readLong();
short s=in.readShort();
float f=in.readFloat();
double d=in.readDouble();
String str=in.readUTF();
in.close();
System.out.println("["+b+"],"+"["+bt+"],"+"["+c+"],"+"["+i+"],"+"["+l+"],"+"["+s+"],"+"["+f+"],"+"["+d+"],"+"["+str+"]");
}
static void write() throws IOException {
RandomAccessFile out = new RandomAccessFile(file, "rw");
for(int i = 0; i < 7; i++){
out.writeDouble(i*1.414);
}
out.writeUTF("The end of the file");
out.writeBoolean(true);
out.writeByte(56);
out.writeChar('a');
out.writeInt(56);
out.writeLong(56l);
out.writeShort(56);
out.writeFloat(56f);
out.writeDouble(3.14159);
out.writeUTF("That was pi");
out.close();
}
public static void main(String[] args) throws IOException {
write();
display();
}
}
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
@SuppressWarnings("serial")
public class TextFile extends ArrayList<String> {
// 读文件,返回String
public static String read(String fileName) {
StringBuilder sb = new StringBuilder();
try {
BufferedReader in= new BufferedReader(new FileReader(new File(fileName).getAbsoluteFile()));
try {
String s;
while((s = in.readLine()) != null) {
sb.append(s);
sb.append("\n");
}
} finally {
in.close();
}
} catch(IOException e) {
throw new RuntimeException(e);
}
return sb.toString();
}
// 直接写String进文件
public static void write(String fileName, String text) {
try {
PrintWriter out = new PrintWriter(new File(fileName).getAbsoluteFile());
try {
out.print(text);
} finally {
out.close();
}
} catch(IOException e) {
throw new RuntimeException(e);
}
}
// 构造器。 切割成String的ArrayList。
public TextFile(String fileName, String splitter) {
super(Arrays.asList(read(fileName).split(splitter)));
if(get(0).equals("")){remove(0);}
}
// 构造器。按行切割。
public TextFile(String fileName) {
this(fileName, "\n");
}
public void write(String fileName) {
try {
PrintWriter out = new PrintWriter(new File(fileName).getAbsoluteFile());
try {
for(String item : this){
out.println(item);
}
} finally {
out.close();
}
} catch(IOException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
String file = read("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/TextFile.java");
write("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/TextFile.txt", file);
TextFile text = new TextFile("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/TextFile.txt");
text.write("test2.txt");
// Break into unique sorted list of words:
TreeSet<String> words = new TreeSet<String>(new TextFile("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/TextFile.java", "\\W+"));
// Display the capitalized words:
System.out.println(words.headSet("a"));
}
}
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
public class Exercise17{
public static void main(String[] args){
TextFile tf=new TextFile("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/TextFile.java","");
Map<Character, Integer> map=new HashMap<Character,Integer>();
for(String s:tf){
Character c=s.charAt(0);
if(map.containsKey(c)){
map.put(c,map.get(c)+1);
}else{
map.put(c,1);
}
}
System.out.println(map);
}
}
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
@SuppressWarnings("serial")
public class Exercise18 extends ArrayList<String> {
// 读文件,返回String
public static String read(String fileName) throws IOException{
StringBuilder sb = new StringBuilder();
BufferedReader in= new BufferedReader(new FileReader(new File(fileName).getAbsoluteFile()));
String s;
try{
while((s = in.readLine()) != null) {
sb.append(s);
sb.append("\n");
}
}finally{
in.close();
}
return sb.toString();
}
// 直接写String进文件
public static void write(String fileName, String text) throws IOException{
PrintWriter out = new PrintWriter(new File(fileName).getAbsoluteFile());
try{
out.print(text);
}finally{
out.close();
}
}
// 构造器。 切割成String的ArrayList。
public Exercise18(String fileName, String splitter) throws IOException{
super(Arrays.asList(read(fileName).split(splitter)));
if(get(0).equals("")){remove(0);}
}
// 构造器。按行切割。
public Exercise18(String fileName) throws IOException{
this(fileName, "\n");
}
public void write(String fileName) throws IOException{
PrintWriter out = new PrintWriter(new File(fileName).getAbsoluteFile());
try{
for(String item : this){
out.println(item);
}
}finally{
out.close();
}
}
public static void main(String[] args) throws IOException{
String file = read("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/TextFile.java");
write("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/TextFile.txt", file);
Exercise18 text = new Exercise18("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/TextFile.txt");
text.write("test2.txt");
// Break into unique sorted list of words:
TreeSet<String> words = new TreeSet<String>(new TextFile("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/TextFile.java", "\\W+"));
// Display the capitalized words:
System.out.println(words.headSet("a"));
}
}
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
public class BinaryFile {
public static byte[] read(File bFile) throws IOException{
BufferedInputStream bf = new BufferedInputStream(new FileInputStream(bFile));
try {
byte[] data = new byte[bf.available()];
bf.read(data);
return data;
} finally {
bf.close();
}
}
public static byte[] read(String bFile) throws IOException {
return read(new File(bFile).getAbsoluteFile());
}
}
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
public class Exercise19 {
public static void main(String[] args) throws IOException{
byte[] bytes=BinaryFile.read("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/BinaryFile.java");
Map<Byte,Integer> map=new HashMap<Byte,Integer>();
for(int i=0;i<bytes.length;i++){
byte b=bytes[i];
if(map.containsKey(b)){
map.put(b,map.get(b)+1);
}else{
map.put(b,1);
}
}
System.out.println(map);
}
}
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
import java.util.regex.*;
public class Directory{
private List<File> files=new ArrayList<File>();
private List<File> dirs=new ArrayList<File>();
private int size=0;
private int lines=0;
public Directory(){this(".");}
public Directory(String dir){this(new File(dir));}
public Directory(File dir){this(dir,".*");}
public Directory(String dir, String regex){this(new File(dir),regex);}
public Directory(File dir,String regex){
recurseDirs(dir,new FilenameFilter(){
private Pattern p=Pattern.compile(regex);
public boolean accept(File dir,String name){
return p.matcher(name).matches();
}
});
statistic();
}
public Directory(File dir,FilenameFilter filter){
recurseDirs(dir,filter);
statistic();
}
//递归收集文件
public void recurseDirs(File dir, FilenameFilter filter){
File[] children=dir.listFiles();
if(children==null || children.length==0){return;}
for(File f:children){
if(f.isDirectory()){
dirs.add(f);
recurseDirs(f,filter);
}else{
if(filter.accept(dir,f.getName())){
files.add(f);
}
}
}
}
public void statistic(){
if(files!=null && files.size()>0){
size=files.size();
int num=0;
for(File file:files){
try{
BufferedReader bf=new BufferedReader(new FileReader(file));
while(bf.readLine()!=null){
num++;
}
}catch(Exception e){
System.out.println(file.getName()+" can not open!");
}
}
lines=num;
}
}
public List<File> getFile(){return files;}
public List<File> getDir(){return dirs;}
public void showStatistic(){System.out.println("File number: "+size+"\n"+"Total Lines: "+lines+"\n");}
}
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
public class BinaryFile {
public static byte[] read(File bFile) throws IOException{
BufferedInputStream bf = new BufferedInputStream(new FileInputStream(bFile));
try {
byte[] data = new byte[bf.available()];
bf.read(data);
return data;
} finally {
bf.close();
}
}
public static byte[] read(String bFile) throws IOException {
return read(new File(bFile).getAbsoluteFile());
}
}
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
public class Exercise20 {
public static void main(String[] args) throws IOException{
Directory dir=new Directory("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18");
List<File> files=dir.getFile();
if(!files.isEmpty()){
for(File file:files){
if(file.getName().endsWith(".class")){
byte[] bytes=BinaryFile.read(file);
String one=Integer.toHexString(((Byte)bytes[0]).intValue());
String two=Integer.toHexString(((Byte)bytes[1]).intValue());
String three=Integer.toHexString(((Byte)bytes[2]).intValue());
String four=Integer.toHexString(((Byte)bytes[3]).intValue());
System.out.println(one+two+three+four);
}
}
}
}
}
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
public class Exercise21{
public static void main(String[] args) throws IOException{
PrintStream console=System.out;
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
PrintStream ps=new PrintStream(new BufferedOutputStream(new FileOutputStream("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/Exercise21.txt")));
System.setOut(ps);
String s;
while((s=br.readLine())!=null && s.length()!=0){
s=s.toUpperCase();
System.out.println(s.toUpperCase());
}
ps.close();
System.setOut(console);
}
}
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
public class Exercise22 {
public static List<String> command(String command) {
boolean err = false;
List<String> list=new ArrayList<String>();
try {
Process process = new ProcessBuilder(command.split(" ")).start();
BufferedReader results = new BufferedReader(new InputStreamReader(process.getInputStream()));
String s;
while((s = results.readLine())!= null){
list.add(s);
}
BufferedReader errors = new BufferedReader(new InputStreamReader(process.getErrorStream()));
// Report errors and return nonzero value
// to calling process if there are problems:
while((s = errors.readLine())!= null) {
System.err.println(s);
err = true;
}
} catch(Exception e) {
// Compensate for Windows 2000, which throws an
// exception for the default command line:
if(!command.startsWith("CMD /C")){
command("CMD /C " + command);
}else{
throw new RuntimeException(e);
}
}
if(err){
throw new OSExecuteException("Errors executing " + command);
}
return list;
}
public static void main(String[] args){
List<String> reverse=Exercise22.command("javap com.ciaoshen.thinkinjava.chapter18.OSExecute");
System.out.println(reverse);
}
}
这题有个坑: asCharBuffer()方法,默认以UTF-16BE来解码byteBuffer里的字节。每个字符2字节。而String # getBytes()使用我系统默认编码方式:而我系统的默认编码方式是:UTF-8。英语字母一个字节。
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
public class Exercise23{
private static final int BSIZE=1024;
private File f;
public Exercise23(String s) throws IOException{
this(new File(s));
}
public Exercise23(File f) throws IOException{
this.f=f;
if(!f.exists()){
f.createNewFile();
}
}
public void write(String s) throws IOException{
FileChannel fc=new FileOutputStream(f).getChannel();
//method 1 to create and fill a ByteBuffer
ByteBuffer bf1=ByteBuffer.wrap(s.substring(0,s.length()/2).getBytes());
//method 2 to create and fill a ByteBuffer
ByteBuffer bf2=ByteBuffer.allocate(BSIZE);
bf2.put(s.substring(s.length()/2).getBytes());
bf2.flip(); //Important!!
fc.write(bf1);
fc.write(bf2);
fc.close();
}
public byte[] read() throws IOException{
FileChannel fc=new FileInputStream(f).getChannel();
ByteBuffer bf=ByteBuffer.allocate(BSIZE);
fc.read(bf);
bf.flip();
byte[] ba=new byte[bf.limit()];
int index=0;
while(bf.hasRemaining()){
ba[index]=bf.get();
index++;
}
fc.close();
return ba;
}
//输出乱码
public char[] badReadAsChars() throws IOException{
FileChannel fc=new FileInputStream(f).getChannel();
ByteBuffer bf=ByteBuffer.allocate(BSIZE);
fc.read(bf);
bf.flip();
/**
* 坑在这儿:
* asCharBuffer()方法,默认以UTF-16BE来解码byteBuffer里的字节。每个字符2字节。
* 而String # getBytes()使用我系统默认编码方式:
* 而我系统的默认编码方式是:UTF-8。英语字母一个字节。
*/
CharBuffer cf=bf.asCharBuffer();
char[] ca=new char[cf.limit()];
int index=0;
while(cf.hasRemaining()){
ca[index]=cf.get();
index++;
}
fc.close();
return ca;
}
//这种解决方案比较好。因为从头到尾使用系统默认的编码标准,系统打开本地写入的文件的时候,不会显示乱码。
public char[] readAsChars() throws IOException{
FileChannel fc=new FileInputStream(f).getChannel();
ByteBuffer bf=ByteBuffer.allocate(BSIZE);
fc.read(bf);
bf.flip();
//解决方法一
//要么在byte解码到char的时候,找到系统默认的编码标准。
CharBuffer cf=Charset.forName(System.getProperty("file.encoding")).decode(bf);
char[] ca=new char[cf.limit()];
int index=0;
while(cf.hasRemaining()){
ca[index]=cf.get();
index++;
}
fc.close();
return ca;
}
public static void main(String[] args) throws IOException{
//系统默认编码是UTF-8
System.out.println(System.getProperty("file.encoding"));
Exercise23 gc=new Exercise23("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/Exercise23.txt");
gc.write("Hello World!");
byte[] ba=gc.read();
System.out.println("Bytes >>> "+Arrays.toString(ba));
System.out.println("String >>> "+new String(ba));
char[] ca=gc.readAsChars();
System.out.println("Chars >>> "+Arrays.toString(ca));
System.out.println("String >>> "+new String(ca));
}
}
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
public class Exercise24{
private static final int BSIZE = 1024;
public static void main(String[] args) {
ByteBuffer bb = ByteBuffer.allocate(BSIZE);
DoubleBuffer db = bb.asDoubleBuffer();
db.put(new double[]{ 11.1, 42.2, 47.3, 99.4, 143.5, 811.6, 1016.7 });
System.out.println(db.get(3));
db.put(new double[] {3.8, 1811.9});
db.flip();
while(db.hasRemaining()) {
double d = db.get();
System.out.println(d);
}
}
}
确实如书中所说,ByteBuffer.allocateDirect()可以提高后面的读写效率,但allocateDirect()本身启动时间比allocate()慢。
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
public class Exercise25{
private static final int BSIZE = 1048576;
private abstract static class Tester {
private String name;
public Tester(String name) { this.name = name; }
public void runTest() {
System.out.print(name + ": ");
try {
long start = System.nanoTime();
long mid=test();
long end=System.nanoTime();
double firstHalf = mid-start;
double secondHalf = end - mid;
System.out.format("%.2f %.2f\n", firstHalf/1.0e6, secondHalf/1.0e6);
} catch(IOException e) {
throw new RuntimeException(e);
}
}
public abstract long test() throws IOException;
}
private static Tester[] tests={
new Tester("Allocate") {
public long test() throws IOException{
ByteBuffer bb = ByteBuffer.allocate(BSIZE);
long mid=System.nanoTime();
DoubleBuffer db = bb.asDoubleBuffer();
for(int i=0;i<BSIZE/8;i++){
db.put((double)i);
}
return mid;
}
},
new Tester("AllocateDirect") {
public long test() throws IOException{
ByteBuffer bb = ByteBuffer.allocateDirect(BSIZE);
long mid=System.nanoTime();
DoubleBuffer db = bb.asDoubleBuffer();
for(int i=0;i<BSIZE/8;i++){
db.put((double)i);
}
return mid;
}
}
};
public static void main(String[] args) {
for(Tester test : tests){
test.runTest();
}
}
}
Allocate: 0,62 10,53
AllocateDirect: 0,96 4,52
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.util.regex.*;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
public class Exercise26{
public static void jgrep(String regex, String path) throws IOException{
Pattern p = Pattern.compile(regex);
// Iterate through the lines of the input file:
int index = 0;
Matcher m = p.matcher("");
FileChannel fc=new RandomAccessFile(path, "r").getChannel();
CharBuffer cb=Charset.forName(System.getProperty("file.encoding")).decode(fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()));
StringBuilder sb=new StringBuilder();
Character c;
while(cb.hasRemaining()){
c=cb.get();
sb.append(c);
if(c.equals('\n')){
m.reset(sb.toString());
while(m.find()){
System.out.println(index++ + ": " + m.group() + ": " + m.start());
}
sb.delete(0,sb.length());
}
}
fc.close();
}
public static void main(String[] args) throws IOException{
jgrep("if","/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/Exercise26.java");
}
}
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
import java.nio.*;
public class Exercise27{
private static Random rander=new Random();
public static class Pregnant implements Serializable{
private static final long serialVersionUID=1L;
private int month;
private Baby theBaby;
public Pregnant(int month){
this.month=month;
theBaby=new Baby(month);
}
public String toString(){return "Pregnant for "+month+" month! Baby weights "+theBaby.getWeight()+" KG!";}
}
public static class Baby implements Serializable{
private static final long serialVersionUID=1L;
private float weight;
private int sex;
public Baby(int month){
weight=rander.nextFloat()*rander.nextInt(2)+(float)month;
sex=rander.nextInt(1);
}
public float getWeight(){return weight;}
}
public static void main(String[] args) throws IOException, ClassNotFoundException{
Exercise27.Pregnant p=new Pregnant(5);
File f=new File("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/Exercise27.txt");
if(!f.exists()){
f.createNewFile();
}
//序列化
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream(f));
out.writeObject(p);
//反序列化
ObjectInputStream in=new ObjectInputStream(new FileInputStream(f));
Pregnant p2=(Pregnant)in.readObject();
System.out.println(p2);
}
}
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
import java.nio.*;
public class Exercise28 {
public static class BlipCheck implements Externalizable {
private static final long serialVersionUID=1L;
//这里默认构造器会被外部调用,所以必须是public的。
//注释掉之后系统会自动创建一个默认构造器,默认是public的。
/*
BlipCheck() {
System.out.println("Blip2 Constructor");
}
*/
public void writeExternal(ObjectOutput out) throws IOException {
System.out.println("Blip2.writeExternal");
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
System.out.println("Blip2.readExternal");
}
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
System.out.println("Constructing objects:");
Exercise28.BlipCheck bc = new Exercise28.BlipCheck();
ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/Exercise28.txt"));
System.out.println("Saving objects:");
o.writeObject(bc);
o.close();
// Now get them back:
ObjectInputStream in = new ObjectInputStream(new FileInputStream("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/Exercise28.txt"));
System.out.println("Recovering BlipCheck:");
bc = (BlipCheck)in.readObject();
}
}
运行的结果会抛出OptionalDataException。异常的原因是实现Externalizable接口的可序列化类,在序列化和反序列化的时候,都只调用默认的构造器。而这里Blip3的默认构造器没有初始化s和i的值。而在writeExternal和readExternal方法里又没有把s和i补偿序列化。反序列化的结果不完整。s和i始终没有初始化。
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
import java.nio.*;
public class Exercise29 {
public static class Blip3 implements Externalizable {
private static final long serialVersionUID=1L;
private int i;
private String s; // No initialization
public Blip3() {
System.out.println("Blip3 Constructor");
// s, i not initialized
}
public Blip3(String x, int a) {
System.out.println("Blip3(String x, int a)");
s = x;
i = a;
// s & i initialized only in non-default constructor.
}
public String toString() { return s + i; }
public void writeExternal(ObjectOutput out) throws IOException {
System.out.println("Blip3.writeExternal");
// You must do this:
//out.writeObject(s);
//out.writeInt(i);
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
System.out.println("Blip3.readExternal");
// You must do this:
s = (String)in.readObject();
i = in.readInt();
}
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
System.out.println("Constructing objects:");
Exercise29.Blip3 b3 = new Exercise29.Blip3("A String ", 47);
System.out.println(b3);
ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/Exercise29.txt"));
System.out.println("Saving object:");
o.writeObject(b3);
o.close();
// Now get it back:
ObjectInputStream in = new ObjectInputStream(new FileInputStream("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/Exercise29.txt"));
System.out.println("Recovering b3:");
b3 = (Blip3)in.readObject();
// System.out.println(b3); //!!OptionalDataException
}
}
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.io.*;
import java.nio.*;
public class Exercise30 {
public abstract static class Shape implements Serializable {
protected static final long serialVersionUID=1L;
public static final int RED = 1, BLUE = 2, GREEN = 3;
public static int color=0;
private int xPos, yPos, dimension;
private static Random rand = new Random(47);
private static int counter = 0;
public abstract void setColor(int newColor);
public abstract int getColor();
public Shape(int xVal, int yVal, int dim) {
xPos = xVal;
yPos = yVal;
dimension = dim;
}
public String toString() {
return getClass() +
"color[" + getColor() + "] xPos[" + xPos +
"] yPos[" + yPos + "] dim[" + dimension + "]\n";
}
public static Shape randomFactory() {
int xVal = rand.nextInt(100);
int yVal = rand.nextInt(100);
int dim = rand.nextInt(100);
switch(counter++ % 3) {
default:
case 0: return new Circle(xVal, yVal, dim);
case 1: return new Square(xVal, yVal, dim);
case 2: return new Line(xVal, yVal, dim);
}
}
public static void serializeStaticState(ObjectOutputStream os) throws IOException { os.writeInt(color); }
public static void deserializeStaticState(ObjectInputStream os) throws IOException { color = os.readInt(); }
}
public static class Circle extends Shape {
protected static final long serialVersionUID=1L;
public Circle(int xVal, int yVal, int dim) {
super(xVal, yVal, dim);
color=RED;
}
public void setColor(int newColor) { color = newColor; }
public int getColor() { return color; }
}
public static class Square extends Shape {
protected static final long serialVersionUID=1L;
public Square(int xVal, int yVal, int dim) {
super(xVal, yVal, dim);
color = RED;
}
public void setColor(int newColor) { color = newColor; }
public int getColor() { return color; }
}
public static class Line extends Shape {
protected static final long serialVersionUID=1L;
public Line(int xVal, int yVal, int dim) {
super(xVal, yVal, dim);
color=RED;
}
public void setColor(int newColor) { color = newColor; }
public int getColor() { return color; }
}
@SuppressWarnings("unchecked")
public static void main(String[] args) throws Exception {
ObjectInputStream in = new ObjectInputStream(new FileInputStream("/Users/Wei/java/com/ciaoshen/thinkinjava/chapter18/CADState.txt"));
// Read in the same order they were written:
List<Class<? extends Exercise30.Shape>> shapeTypes = (List<Class<? extends Exercise30.Shape>>)in.readObject();
Exercise30.Line.deserializeStaticState(in);
List<Exercise30.Shape> shapes = (List<Exercise30.Shape>)in.readObject();
System.out.println(shapes);
}
}
涉及外部库Elliote Rusty Harold的XOM。跳过。
package com.ciaoshen.thinkinjava.chapter18;
import java.util.*;
import java.util.prefs.*;
public class Exercise33 {
public static void main(String[] args) throws BackingStoreException{
Preferences root=Preferences.userRoot();
root.clear(); //remove the previous enregistrer
byte[] info=root.getByteArray("base directory","/Users".getBytes());
System.out.println(new String(info));
info="/Users/Wei".getBytes();
root.putByteArray("base directory",info);
System.out.println(new String(root.getByteArray("base directory","/Users".getBytes())));
}
}